﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;

namespace UMLTouch.Core.Entities
{
    public class Stroke : System.Windows.Ink.Stroke, IEngineEntity, IEnumerable<Point>
    {
        #region Fields

        List<Point> _points;
        double _lenght;
        bool _isDirty;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the Enumeration of points which constitue the stroke.
        /// </summary>
        public IEnumerable<Point> Points
        {
            get { return _points; }
        }
        /// <summary>
        /// Gets or Sets a boolean value to specifie if the stroke is closed or not.
        /// </summary>
        public Boolean IsClosed { get; set; }
        /// <summary>
        /// Gets the Length of the Stroke
        /// </summary>
        public double Length
        {
            get
            {
                if (_isDirty)
                {
                    Clean();
                }
                return _lenght;
            }
        }
        /// <summary>
        /// Gets the Average distance between two points of the stroke
        /// </summary>
        public double PointDistanceAvg
        {
            get
            {
                return Length / Count;
            }
        }
        /// <summary>
        /// Gets the number of points in the stroke
        /// </summary>
        public int Count
        {
            get
            {
                return _points.Count;
            }
        }
        /// <summary>
        /// Get the point at the index i
        /// </summary>

        public Point this[int i]
        {
            get
            {
                return _points[i];
            }
            set
            {
                _points[i] = value;
            }
        }
        #endregion

        #region Constructors

        public Stroke(System.Windows.Ink.Stroke baseStroke)
            : base(baseStroke.StylusPoints)
        {
         
            _points = baseStroke.GetBezierStylusPoints().ToList().ConvertAll(sp => sp.ToPoint());
            
            IsClosed = false;
            _isDirty = true;
        }

        //protected Stroke()
        //{
        //    _points = new List<Point>();
        //    BaseStroke = null;
        //    IsClosed = false;
        //    _isDirty = true;
        //} 

        #endregion

        #region Methods

        /// <summary>
        /// Adds a point to the stroke
        /// </summary>
        /// <param name="p">the point to add</param>
        public void Add(Point p)
        {
            _points.Add(p);
            //the collection of points has been modified, so the object
            // is set as "dirty" to force recalculation of the averages distance and Length values.
            _isDirty = true;
           
        }

        /// <summary>
        /// Removes a point from the stroke
        /// </summary>
        /// <param name="p">the point to remove</param>
        public void Remove(Point p)
        {
            _points.Remove(p);
            //the collection of points has been modified, so the object
            // is set as "dirty" to force recalculation of the averages distance and Length values.
            _isDirty = true;
        }

        /// <summary>
        /// Gets the first point of the stroke
        /// </summary>
        /// <returns></returns>
        public Point First()
        {
            return _points.First();
        }

        /// <summary>
        /// Gets the last point of the stroke
        /// </summary>
        /// <returns></returns>
        public Point Last()
        {
            return _points.Last();
        }


        /// <summary>
        /// Insert a point at the given position
        /// </summary>
        /// <param name="p">the position where the point shall be added</param>
        /// <param name="point">the point to add</param>
        public void Insert(int p, Point point)
        {
            _points.Insert(p, point);
            //the collection of points has been modified, so the object
            // is set as "dirty" to force recalculation of the averages distance and Length values.
            _isDirty = true;
        }


        /// <summary>
        /// Method called to clean the object when _IsDirty is true
        /// </summary>
        private void Clean()
        {
            _lenght = GetLength();
            _isDirty = false;
        }

        /// <summary>
        /// Gets the length of the stroke by making the sum of the distance between every two successive points.
        /// </summary>
        /// <returns>the lenght of the stroke</returns>
        private double GetLength()
        {
            double strokeLenght = 0;
            for (int i = 1; i < _points.Count; i++)
            {
                Point pred = _points[i - 1];
                Point curr = _points[i];
                //convert the two points into a vector and returns the lenght of the vector.
                strokeLenght += Point.Subtract(pred, curr).Length;
            }
            return strokeLenght;
        }



        /// <summary>
        /// returns the distance between the first point and the last point of the stroke
        /// </summary>
        public double DistanceFirstToLast()
        {
            //convert the two points into a vector and returns the lenght of the vector.
            return Point.Subtract(First(), Last()).Length;
        }

        /// <summary>
        /// Gets the Enumerator of the points
        /// </summary>
        /// <returns></returns>
        public IEnumerator<Point> GetEnumerator()
        {
            return _points.GetEnumerator();
        }

        /// <summary>
        /// Gets the Enumerator of the points
        /// </summary>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }


        #endregion


    }
}
